#include "gamemodel.h"
#include "player.h"
#include <ClanLib/core.h>
#include <stdio.h>

GameModel* GameModel::instance = NULL;

GameModel::GameModel(void) {
	this->isGameRunning = true;
	this->currentAnimationStep = 0;
	this->resourceManager = ResourceManager::getInstance();
	this->continueJump = false;
	this->fallDownActive = false;
	this->sound = new Sound();
	this->hasKebab = false;
	this->gamestate = GameState::INGAME;
}

GameModel::~GameModel(void) {

}

GameModel* GameModel::getInstance() {
	if (instance == NULL) {
		instance = new GameModel();
	}
	return instance;
}

GameState::GameState GameModel::getGameState() {
	return this->gamestate;
}

void GameModel::addEnemy(Enemy* enemy) {
	this->enemies.push_back(enemy);
}

void GameModel::checkEnemyCollision() {
	Enemy* enemy;
	Player* player = resourceManager->getPlayer();
	Level* lvl = resourceManager->getLevel();

	float playerPosX = player->getPosX();
	float playerPosY = player->getPosY();

	for (unsigned int i = 0; i < enemies.size(); i++) {
		enemy = enemies.at(i);

		float enemyPosX = enemy->getPosX();
		float enemyPosY = enemy->getPosY();
		float enemyPosX1 = enemyPosX - 0.3f;
		float enemyPosX2 = enemyPosX - 1.6f;
		float enemyPosY1 = enemyPosY - 0.01f;
		float enemyPosY2 = enemyPosY - 1.3f;

		if (player->getHasHabanero() == false) {
			if ((enemyPosX1 > playerPosX) && (enemyPosX2 < playerPosX)
					&& (enemyPosY == playerPosY)) {
				sound->catched();
				player->setLifes(player->getLifes() - 1);
				player->setIsDead(true);
				if (player->getLifes() > 0) {
					player->setPosX(static_cast<float> (lvl->getStartX()));
					player->setPosY(static_cast<float> (lvl->getStartY()));
				} else {
					this->gamestate = GameState::GAME_OVER;
				}

			} else if ((enemyPosX1 > playerPosX) && (enemyPosX2 < playerPosX)
					&& (enemyPosY1 > playerPosY) && (enemyPosY2 < playerPosY)) {
				//delete enemy
				sound->enemyDown();
				enemies.erase(enemies.begin() + i);
			}
		} else {
			if ((enemyPosX1 > playerPosX) && (enemyPosX2 < playerPosX)
					&& (enemyPosY == playerPosY)) {
				sound->enemyDown();
				enemies.erase(enemies.begin() + i);
			} else if (enemyPosX1 > playerPosX && enemyPosX2 < playerPosX
					&& enemyPosY1 > playerPosY && enemyPosY2 < playerPosY) {
				sound->enemyDown();
				enemies.erase(enemies.begin() + i);
			}
		}
	}
}

void GameModel::moveEnemies() {
	Enemy* enemy;
	Player* player = resourceManager->getPlayer();
	Level* lvl = resourceManager->getLevel();
	for (unsigned int i = 0; i < enemies.size(); i++) {
		enemy = enemies.at(i);
		float startPosX = enemy->getStartPosX();
		int range = enemy->getRange();
		float currPosX = enemy->getPosX();

		float enemyMovingSpeed = enemy->getMovingSpeed();

		// range ueberpruefen
		if (enemy->getMovingDirection() == EnemyDirection::RIGHT) {
			if (abs(currPosX - startPosX) > range) {
				enemy->setMovingDirection(EnemyDirection::LEFT);
			}
		} else if (enemy->getMovingDirection() == EnemyDirection::LEFT) {
			if (abs(currPosX - startPosX) > range) {
				enemy->setMovingDirection(EnemyDirection::RIGHT);
			}
		}

		EnemyDirection::MovingDirection direction = enemy->getMovingDirection();
		// neue position berechnen
		float newPosX;
		if (direction == EnemyDirection::RIGHT) {
			newPosX = currPosX - enemyMovingSpeed;
		} else if (direction == EnemyDirection::LEFT) {
			newPosX = currPosX + enemyMovingSpeed;
		}

		enemy->setPosX(newPosX);

	}
}

std::vector<Enemy*> GameModel::getEnemies() {
	moveEnemies();
	checkEnemyCollision();
	return this->enemies;
}

void GameModel::playerMoveLeft() {
	Player* player = resourceManager->getPlayer();

	if (checkCollision(CheckDirection::BOTTOM) && !continueJump) {
		fallDownActive = true;
	}

	if (checkCollision(CheckDirection::LEFT)) {
		float currPosX = player->getPosX();
		if (player->getHasHabanero()) {
			player->setPosX(currPosX - MOVING_SPEED * 2);
		} else {
			player->setPosX(currPosX - MOVING_SPEED);
		}
		player->setPlayerState(PlayerDirection::LEFT);
		player->setLastPlayerDirection(PlayerDirection::LEFT);
	}
}

void GameModel::playerMoveRight() {
	Player* player = resourceManager->getPlayer();

	if (checkCollision(CheckDirection::BOTTOM) && !continueJump) {
		fallDownActive = true;
	}

	if (checkCollision(CheckDirection::RIGHT)) {
		float currPosX = player->getPosX();
		if (player->getHasHabanero()) {
			player->setPosX(currPosX + MOVING_SPEED * 2);
		} else {
			player->setPosX(currPosX + MOVING_SPEED);
		}
		player->setPlayerState(PlayerDirection::RIGHT);
		player->setLastPlayerDirection(PlayerDirection::RIGHT);
	}
}

void GameModel::playerJump() {
	if (!fallDownActive)
		continueJump = executePlayerJump();
}

bool GameModel::executePlayerJump() {
	Player* player = resourceManager->getPlayer();

	if (checkCollision(CheckDirection::BOTTOM) || currentAnimationStep == 0) {
		playerJumpDoNextAnimationStep();
		return true;
	} else {
		if (player->getPlayerState() != PlayerDirection::JUMP) {
			player->setPlayerState(PlayerDirection::JUMP);
			playerJumpDoNextAnimationStep();
			return true;
		} else {
			currentAnimationStep = 0;
			player->setPlayerState(PlayerDirection::NONE);
			player->setPosY((float) ((int) (player->getPosY())));
			return false;
		}
	}
}

void GameModel::playerJumpDoNextAnimationStep() {
	Player* player = resourceManager->getPlayer();
	static float playerOriginalY;

	if (currentAnimationStep == 0) {
		playerOriginalY = (float) ((int) (player->getPosY()));
		fallDownActive = false;
	}

	if (!checkCollision(CheckDirection::TOP))
		fallDownActive = true;

	if (currentAnimationStep < jumpAnimationCount && !fallDownActive) {
		player->setPosY(playerOriginalY - jumpAnimation[currentAnimationStep]);
	} else {
		fallDownActive = true;
	}

	currentAnimationStep++;
}

void GameModel::fallDown() {
	Player* player = resourceManager->getPlayer();
	player->setPosY(player->getPosY() + MOVING_SPEED);

	continueJump = false;
	currentAnimationStep = 0;
	player->setPlayerState(PlayerDirection::NONE);

	if (checkCollision(CheckDirection::BOTTOM)) {
		fallDownActive = true;
	} else {
		player->setPosY((float) ((int) (player->getPosY())));
		fallDownActive = false;
	}
}

bool GameModel::isBlockColliding(const char &block, const float collX,
		const float collY) {
	Player* player = resourceManager->getPlayer();
	Level *level = resourceManager->getLevel();
	int x1 = static_cast<int> (collX);
	int y1 = static_cast<int> (collY);

	switch (block) {
	case 'G':
	case 'S':
	case '#':
		return true;
		break;
	case 'C':
		level->setTile(x1, y1, ' ');
		sound->coin();
		player->setScore(player->getScore() + SCORE_COIN);
		return false;
		break;
	case 'H':
		level->setTile(x1, y1, ' ');
		sound->habanero();
		player->setHasHabanero(true);
		return false;
		break;
	case 'K':
		level->setTile(x1, y1, ' ');
		sound->kebab();
		hasKebab = true;
		return false;
		break;
	case 'B':
		level->setTile(x1, y1, ' ');
		sound->beer();
		player->setScore(player->getScore() + SCORE_BEER);
		return false;
		break;
	case 'T':
		level->setTile(x1, y1, ' ');
		sound->brush();
		player->setScore(player->getScore() + SCORE_BRUSH);
		return false;
		break;
	case 'Y':
		if (hasKebab) {
			sound->goal();
			this->gamestate = GameState::FINISHED;
		}
		return false;
		break;
	default:
		return false;
	}
}

bool GameModel::checkCollision(enum CheckDirection::CheckDirection dir) {
	float playerX = resourceManager->getPlayer()->getPosX();
	float playerY = resourceManager->getPlayer()->getPosY();
	Level *level = resourceManager->getLevel();
	char tile1;
	char tile2;

	float coll1X = 0;
	float coll1Y = 0;
	float coll2X = 0;
	float coll2Y = 0;

	switch (dir) {
	case CheckDirection::LEFT:
		coll1X = playerX + 0.3f;
		coll1Y = playerY + 0.5f - COLLISION_VERTICAL;
		coll2X = playerX + 0.3f;
		coll2Y = playerY + 0.5f + COLLISION_VERTICAL;
		tile1 = level->getNearestTile(coll1X, coll1Y);
		tile2 = level->getNearestTile(coll2X, coll2Y);

		break;
	case CheckDirection::RIGHT:
		coll1X = playerX + 1.35f;
		coll1Y = playerY + 0.5f - COLLISION_VERTICAL;
		coll2X = playerX + 1.35f;
		coll2Y = playerY + 0.5f + COLLISION_VERTICAL;
		tile1 = level->getNearestTile(coll1X, coll1Y);
		tile2 = level->getNearestTile(coll2X, coll2Y);

		break;
	case CheckDirection::TOP:
		coll1X = playerX + 0.85f - COLLISION_HORIZONTAL;
		coll1Y = playerY - 0.1f;
		coll2X = playerX + 0.85f + COLLISION_HORIZONTAL;
		coll2Y = playerY - 0.1f;
		tile1 = level->getNearestTile(coll1X, coll1Y);
		tile2 = level->getNearestTile(coll2X, coll2Y);

		break;
	case CheckDirection::BOTTOM:
		coll1X = playerX + 0.85f - COLLISION_HORIZONTAL;
		coll1Y = playerY + 1.0f;
		coll2X = playerX + 0.85f + COLLISION_HORIZONTAL;
		coll2Y = playerY + 1.0f;
		tile1 = level->getNearestTile(coll1X, coll1Y);
		tile2 = level->getNearestTile(coll2X, coll2Y);

		break;
	default:
		break;
	}

	return (!isBlockColliding(tile1, coll1X, coll1Y)) && (!isBlockColliding(
			tile2, coll2X, coll2Y));
}
